#include "volumemask.h"
#include "global.h"
#include "staticfunctions.h"
#include "geometryobjects.h"
#include <QFile>

void VolumeMask::setFilename(QString flnm)
{
  m_maskFilename = flnm;
  m_maskFileManager.setBaseFilename(flnm);
}
void VolumeMask::setDragTextureSize(Vec dts, int dtw, int dth)
{
  m_dragTextureSize = dts;
  m_dragTexWidth = dtw;
  m_dragTexHeight = dth;
}
void VolumeMask::setSubsamplingLevel(int svsl) { m_svsl = svsl; }
void VolumeMask::setSlabSize(int slabsize)
{
  m_slabSize = slabsize;
  m_maskFileManager.setSlabSize(slabsize);
}

void
VolumeMask::setDataSize(Vec dmin, Vec dmax)
{
  m_dataMin = dmin;
  m_dataMax = dmax;
  m_subvolumeSize = m_dataMax - m_dataMin + Vec(1,1,1);
}

VolumeMask::VolumeMask()
{
  m_width = 0;
  m_height = 0;
  m_depth = 0;

  m_bitmask.clear();

  m_sliceTexture = 0;
  m_dragTexture = 0;
  m_sliceTemp = 0;
}

VolumeMask::~VolumeMask()
{
  if (m_sliceTexture)
    delete [] m_sliceTexture;
  m_sliceTexture = 0;

  if (m_dragTexture)
    delete [] m_dragTexture;
  m_dragTexture = 0;

  if (m_sliceTemp)
    delete [] m_sliceTemp;
  m_sliceTemp = 0;

  m_bitmask.clear();

}

void
VolumeMask::setDimensions(int w, int h, int d)
{
  m_width = w;
  m_height = h;
  m_depth = d;

  if (m_sliceTemp)
    delete [] m_sliceTemp;
  m_sliceTemp = new unsigned char [m_width*m_height];

  m_maskFileManager.setDepth(m_depth);
  m_maskFileManager.setWidth(m_width);
  m_maskFileManager.setHeight(m_height);
  m_maskFileManager.setHeaderSize(13);
}

void
VolumeMask::tagAndUpdateBitmask(int tag,
				int minx, int miny, int minz,
				int maxx, int maxy, int maxz,
				QBitArray vbitmask,
				int tminx, int tminy, int tminz,
				int tlenx, int tleny, int tlenz,
				int tnsubX, int tnsubY, int tnsubZ,
				int tlod2,
				QList<Vec> clipPos,
				QList<Vec> clipNormal)
{
  Global::mainWindowUI()->menubar->parentWidget()->\
    setWindowTitle(QString("Tag And Update Mask"));
  Global::progressBar()->show();

  int bidx = 0;
  for(int k=minz; k<=maxz; k++)
    {
      Global::progressBar()->setValue((int)(100.0*(float)(k-minz)/(float)(maxz-minz+1)));
      qApp->processEvents();
      
      uchar *mslice = m_maskFileManager.getSlice(k);
      memcpy(m_sliceTemp, mslice, m_width*m_height);

      // change mask
      for(int y=miny; y<=maxy; y++)
	for(int x=minx; x<=maxx; x++)
	  {
	    Vec po = Vec(x, y, k);
	    bool ok = StaticFunctions::getClip(po, clipPos, clipNormal);

	    if (ok)
	      {
		if (vbitmask.testBit(bidx) &&
		    m_bitmask.testBit(bidx))
		  {
		    int idx = (y*m_height + x);
		    m_sliceTemp[idx] = tag;
		  }
	      }

	    bidx++;
	  }

      // save mask
      m_maskFileManager.setSlice(k, m_sliceTemp);
    }
  
  Global::progressBar()->setValue(100);
  Global::hideProgressBar();
}


void
VolumeMask::updateBitmask(int tag,
			  int minx, int miny, int minz,
			  int maxx, int maxy, int maxz,
			  QBitArray vbitmask,
			  int tminx, int tminy, int tminz,
			  int tlenx, int tleny, int tlenz,
			  int tnsubX, int tnsubY, int tnsubZ,
			  int tlod2)
{
  uchar *tagColors = Global::tagColors();

  Global::mainWindowUI()->menubar->parentWidget()->\
    setWindowTitle(QString("Update Mask"));
  Global::progressBar()->show();

  int bidx = 0;
  for(int k=minz; k<=maxz; k++)
    {
      Global::progressBar()->setValue((int)(100.0*(float)(k-minz)/(float)(maxz-minz+1)));
      qApp->processEvents();
      
      uchar *mslice = m_maskFileManager.getSlice(k);
      memcpy(m_sliceTemp, mslice, m_width*m_height);

      bool maskChanged = false;
      // change mask
      for(int y=miny; y<=maxy; y++)
	for(int x=minx; x<=maxx; x++)
	  {
	    if (vbitmask.testBit(bidx))
	      {
		if (getOpacity(m_sliceTemp,
			       tagColors,
			       x, y))
		  {
		    int idx = (y*m_height + x);
		    m_sliceTemp[idx] = tag;
		    maskChanged = true;
		  }
	      }

	    bidx++;
	  }

      if (maskChanged)
	m_maskFileManager.setSlice(k, m_sliceTemp);
    }
  
  Global::progressBar()->setValue(100);
  Global::hideProgressBar();
}


void
VolumeMask::fillVolumeWithUpdateMask(int tag,
				     QList<Vec> pbc,
				     int rad,
				     int minx, int miny, int minz,
				     int maxx, int maxy, int maxz,
				     QBitArray vbitmask,
				     int tminx, int tminy, int tminz,
				     int tlenx, int tleny, int tlenz,
				     int tnsubX, int tnsubY, int tnsubZ,
				     int tlod2,
				     QList<Vec> clipPos,
				     QList<Vec> clipNormal)
{
  createBitmask2(pbc,
		 rad,
		 minx, miny, minz,
		 maxx, maxy, maxz);

  tagAndUpdateBitmask(tag,
		      minx, miny, minz,
		      maxx, maxy, maxz,
		      vbitmask,
		      tminx,  tminy,  tminz,
		      tlenx,  tleny,  tlenz,
		      tnsubX, tnsubY, tnsubZ,
		      tlod2,
		      clipPos, clipNormal);
}


void
VolumeMask::tagVolumeWithUpdateMask(int tag,
				    QList<Vec> pbc,
				    QList<Vec> pbs,
				    int minx, int miny, int minz,
				    int maxx, int maxy, int maxz,
				    QBitArray vbitmask,
				    int tminx, int tminy, int tminz,
				    int tlenx, int tleny, int tlenz,
				    int tnsubX, int tnsubY, int tnsubZ,
				    int tlod2,
				    QList<Vec> clipPos,
				    QList<Vec> clipNormal)
{
  createBitmask1(pbc,
		 pbs,
		 vbitmask,
		 minx, miny, minz,
		 maxx, maxy, maxz);

//  //-----------------------------
//  // dilate followed by erode will fill up the
//  // holes if any
//  dilateBitmask(1,
//		minx, miny, minz,
//		maxx, maxy, maxz);
//  erodeBitmask(1,
//	       minx, miny, minz,
//	       maxx, maxy, maxz);
//  //-----------------------------

  tagAndUpdateBitmask(tag,
		      minx, miny, minz,
		      maxx, maxy, maxz,
		      vbitmask,
		      tminx,  tminy,  tminz,
		      tlenx,  tleny,  tlenz,
		      tnsubX, tnsubY, tnsubZ,
		      tlod2,
		      clipPos,
		      clipNormal);
}

void
VolumeMask::tagVolume(int tag, bool opaqueRegion,
		      Vec pbc, Vec pbs,
		      unsigned char* lut,
		      int minx, int miny,
		      int maxx, int maxy,
		      int k, unsigned char *tmp,
		      QList<Vec> clipPos,
		      QList<Vec> clipNormal)
{
  uchar *tagColors = Global::tagColors();

  uchar *mslice = m_maskFileManager.getSlice(k);
  memcpy(m_sliceTemp, mslice, m_width*m_height);

  for(int y=miny; y<=maxy; y++)
    for(int x=minx; x<=maxx; x++)
      {
	Vec po = Vec(x,y,k);	
	Vec pos = po-pbc;
	pos = VECDIVIDE(pos, pbs);
	float len = pos*pos;
	if (len < 1)
	  {

	    bool op = getOpacity(m_sliceTemp,
				 tmp,
				 lut,
				 tagColors,
				 x, y);
	
	    op &= StaticFunctions::getClip(po, clipPos, clipNormal);

	    if (!(opaqueRegion ^ op))	
	      {
		int idx = (y*m_height + x);
		m_sliceTemp[idx] = tag;
	      }

	  }
      }

  m_maskFileManager.setSlice(k, m_sliceTemp);
}


void
VolumeMask::tagVolume(int tag, bool opaqueRegion,
		      unsigned char* lut,
		      int minx, int miny,
		      int maxx, int maxy,
		      int k, unsigned char *tmp,
		      QList<Vec> clipPos,
		      QList<Vec> clipNormal)
{
  uchar *tagColors = Global::tagColors();

  uchar *mslice = m_maskFileManager.getSlice(k);
  memcpy(m_sliceTemp, mslice, m_width*m_height);

  for(int y=miny; y<=maxy; y++)
    for(int x=minx; x<=maxx; x++)
      {
	bool op = getOpacity(m_sliceTemp,
			     tmp,
			     lut,
			     tagColors,
			     x, y);

	Vec po = Vec(x, y, k);
	op &= StaticFunctions::getClip(po, clipPos, clipNormal);
	
	if (!(opaqueRegion ^ op))
	  {
	    int idx = (y*m_height + x);
	    m_sliceTemp[idx] = tag;
	  }
      }
  
  m_maskFileManager.setSlice(k, m_sliceTemp);
}

void
VolumeMask::tagUsingBitmask(int tag, bool opaqueRegion,
			    unsigned char* lut,
			    int minx, int miny,
			    int maxx, int maxy,
			    int k, unsigned char *tmp,
			    QBitArray vbitmask,
			    int z,
			    int bmx, int bmy, int bmz,
			    QList<Vec> clipPos,
			    QList<Vec> clipNormal)
{
  uchar *tagColors = Global::tagColors();

  uchar *mslice = m_maskFileManager.getSlice(k);
  memcpy(m_sliceTemp, mslice, m_width*m_height);

  Vec pbc = GeometryObjects::paintball()->position();
  Vec pbs = GeometryObjects::paintball()->size()/2;

  int bidx = z*bmy*bmx;
  for(int y=miny; y<=maxy; y++)
    for(int x=minx; x<=maxx; x++)
      {
	if (vbitmask.testBit(bidx))
	  {
	    Vec po = Vec(x,y,k);
	    Vec pos = po-pbc;
	    pos = VECDIVIDE(pos, pbs);
	    float len = pos*pos;
	    if (len < 1)
	      {
		bool op = getOpacity(m_sliceTemp,
				     tmp,
				     lut,
				     tagColors,
				     x, y);
	
		op &= StaticFunctions::getClip(po, clipPos, clipNormal);

		if (!(opaqueRegion ^ op))
		  {
		    int idx = (y*m_height + x);
		    m_sliceTemp[idx] = tag;
		  }
	      }
	  }
	bidx++;
      }

  m_maskFileManager.setSlice(k, m_sliceTemp);
}

int
VolumeMask::getVolume(unsigned char tag,
		      int minx, int miny, int minz,
		      int maxx, int maxy, int maxz,
		      QBitArray& vbitmask)
{
  int nonZeroVoxels = 0;

  int bidx = 0;
  for(int k=minz; k<=maxz; k++)
    {
      uchar *mslice = m_maskFileManager.getSlice(k);
      memcpy(m_sliceTemp, mslice, m_width*m_height);

      for(int y=miny; y<=maxy; y++)
	for(int x=minx; x<=maxx; x++)
	  {
	    int idx = (y*m_height + x);
	    if (vbitmask.testBit(bidx) &&
		 m_sliceTemp[idx] == tag )
		nonZeroVoxels++;
	    else
	      vbitmask.toggleBit(bidx);

	    bidx++;
	  }
    }
  return nonZeroVoxels;
}

void
VolumeMask::dilateVolume(int tag, int dilateThickness,
			 int minx, int miny, int minz,
			 int maxx, int maxy, int maxz,
			 QBitArray vbitmask,
			 QList<Vec> clipPos,
			 QList<Vec> clipNormal)

{
  createBitmask(tag,
		minx, miny, minz,
		maxx, maxy, maxz);

  int thickness = dilateThickness;
  while (thickness > 0)
    {
      dilateBitmask(minx, miny, minz,
		    maxx, maxy, maxz);
      thickness --;
    }
  // m_bitmask now contains region dilated by thickness

  Global::mainWindowUI()->menubar->parentWidget()->\
    setWindowTitle(QString("Dilate Volume Mask"));
  Global::progressBar()->show();

  Vec pbc = GeometryObjects::paintball()->position();
  Vec pbs = GeometryObjects::paintball()->size()/2;

  int bidx = 0;
  for(int k=minz; k<=maxz; k++)
    {
      Global::progressBar()->setValue((int)(100.0*(float)(k-minz)/(float)(maxz-minz+1)));
      qApp->processEvents();
      
      uchar *mslice = m_maskFileManager.getSlice(k);
      memcpy(m_sliceTemp, mslice, m_width*m_height);

      for(int y=miny; y<=maxy; y++)
	for(int x=minx; x<=maxx; x++)
	  {
	    Vec po = Vec(x, y, k);
	    if (StaticFunctions::getClip(po, clipPos, clipNormal))
	      {
		Vec pos = po-pbc;
		pos = VECDIVIDE(pos, pbs);
		float len = pos*pos;
		if (len < 1)
		  {
		    if (vbitmask.testBit(bidx) &&
			m_bitmask.testBit(bidx))
		      {
			int idx = (y*m_height + x);
			m_sliceTemp[idx] = tag;
		      }
		  }
	      }
	    bidx++;
	  }

      m_maskFileManager.setSlice(k, m_sliceTemp);
    }
  Global::progressBar()->setValue(100);
  Global::hideProgressBar();
}

void
VolumeMask::erodeVolume(int tag, int erodeThickness,
			int minx, int miny, int minz,
			int maxx, int maxy, int maxz,
			QBitArray vbitmask,
			QList<Vec> clipPos,
			QList<Vec> clipNormal)
{
  createBitmask(tag,
		minx, miny, minz,
		maxx, maxy, maxz);

  int thickness = erodeThickness;
  while (thickness > 0)
    {
      erodeBitmask(minx, miny, minz,
		   maxx, maxy, maxz);
      thickness --;
    }
  // m_bitmask now contains region eroded by thickness

  Global::mainWindowUI()->menubar->parentWidget()->\
    setWindowTitle(QString("Erode Volume Mask"));
  Global::progressBar()->show();


  Vec pbc = GeometryObjects::paintball()->position();
  Vec pbs = GeometryObjects::paintball()->size()/2;

  int bidx = 0;
  for(int k=minz; k<=maxz; k++)
    {
      Global::progressBar()->setValue((int)(100.0*(float)(k-minz)/(float)(maxz-minz+1)));
      qApp->processEvents();
      
      uchar *mslice = m_maskFileManager.getSlice(k);
      memcpy(m_sliceTemp, mslice, m_width*m_height);

      for(int y=miny; y<=maxy; y++)
	for(int x=minx; x<=maxx; x++)
	  {
	    Vec po = Vec(x, y, k);
	    if (StaticFunctions::getClip(po, clipPos, clipNormal))
	      {
		Vec pos = po-pbc;
		pos = pos-pbc;
		pos = VECDIVIDE(pos, pbs);
		float len = pos*pos;
		if (len < 1)
		  {
		    if (vbitmask.testBit(bidx) && // check for 1 bit
			!m_bitmask.testBit(bidx)) // check for 0 bit
		      {
			// reset mask to 0 if the
			// mask value is same as supplied tag value
			int idx = (y*m_height + x);
			if ( m_sliceTemp[idx] == tag)
			  m_sliceTemp[idx] = 0;
		      }
		  }
	      }
	    bidx++;
	  }
      m_maskFileManager.setSlice(k, m_sliceTemp);
    }

  Global::progressBar()->setValue(100);
  Global::hideProgressBar();
}

void
VolumeMask::dilateAllVolume(int tag, int dilateThickness,
			    int minx, int miny, int minz,
			    int maxx, int maxy, int maxz,
			    QBitArray vbitmask,
			    QList<Vec> clipPos,
			    QList<Vec> clipNormal)

{
  createBitmask(tag,
		minx, miny, minz,
		maxx, maxy, maxz);

  int thickness = dilateThickness;
  while (thickness > 0)
    {
      dilateBitmask(minx, miny, minz,
		    maxx, maxy, maxz);
      thickness --;
    }
  // m_bitmask now contains region dilated by thickness

  Global::mainWindowUI()->menubar->parentWidget()->\
    setWindowTitle(QString("Dilate All Volume Mask"));
  Global::progressBar()->show();

  int bidx = 0;
  for(int k=minz; k<=maxz; k++)
    {
      Global::progressBar()->setValue((int)(100.0*(float)(k-minz)/(float)(maxz-minz+1)));
      qApp->processEvents();
      
      uchar *mslice = m_maskFileManager.getSlice(k);
      memcpy(m_sliceTemp, mslice, m_width*m_height);

      for(int y=miny; y<=maxy; y++)
	for(int x=minx; x<=maxx; x++)
	  {
	    Vec po = Vec(x, y, k);
	    if (StaticFunctions::getClip(po, clipPos, clipNormal))
	      {
		if (vbitmask.testBit(bidx) &&
		    m_bitmask.testBit(bidx))
		  {
		    int idx = (y*m_height + x);
		    m_sliceTemp[idx] = tag;
		  }
	      }
	    bidx++;
	  }

      m_maskFileManager.setSlice(k, m_sliceTemp);
    }
  Global::progressBar()->setValue(100);
  Global::hideProgressBar();
}

void
VolumeMask::erodeAllVolume(int tag, int erodeThickness,
			   int minx, int miny, int minz,
			   int maxx, int maxy, int maxz,
			   QBitArray vbitmask,
			   QList<Vec> clipPos,
			   QList<Vec> clipNormal)
{
  createBitmask(tag,
		minx, miny, minz,
		maxx, maxy, maxz);

  int thickness = erodeThickness;
  while (thickness > 0)
    {
      erodeBitmask(minx, miny, minz,
		   maxx, maxy, maxz);
      thickness --;
    }
  // m_bitmask now contains region eroded by thickness

  Global::mainWindowUI()->menubar->parentWidget()->\
    setWindowTitle(QString("Erode All Volume Mask"));
  Global::progressBar()->show();

  int bidx = 0;
  for(int k=minz; k<=maxz; k++)
    {
      Global::progressBar()->setValue((int)(100.0*(float)(k-minz)/(float)(maxz-minz+1)));
      qApp->processEvents();
      
      uchar *mslice = m_maskFileManager.getSlice(k);
      memcpy(m_sliceTemp, mslice, m_width*m_height);

      for(int y=miny; y<=maxy; y++)
	for(int x=minx; x<=maxx; x++)
	  {
	    Vec po = Vec(x, y, k);
	    if (StaticFunctions::getClip(po, clipPos, clipNormal))
	      {
		if (vbitmask.testBit(bidx) && // check for 1 bit
		    !m_bitmask.testBit(bidx)) // check for 0 bit
		  {
		    // reset mask to 0 if the
		    // mask value is same as supplied tag value
		    int idx = (y*m_height + x);
		    if ( m_sliceTemp[idx] == tag)
		      m_sliceTemp[idx] = 0;
		  }
	      }
	    bidx++;
	  }
      
      m_maskFileManager.setSlice(k, m_sliceTemp);
    }
  
  Global::progressBar()->setValue(100);
  Global::hideProgressBar();
}


void
VolumeMask::createBitmask(int tag,
			  int minx, int miny, int minz,
			  int maxx, int maxy, int maxz)
{
  Global::mainWindowUI()->menubar->parentWidget()->\
    setWindowTitle(QString("Identifying Tagged Region"));
  Global::progressBar()->show();

  int bmx = maxx-minx+1;
  int bmy = maxy-miny+1;
  int bmz = maxz-minz+1;

  m_bitmask.resize(bmx*bmy*bmz);  

  int bidx = 0;
  for(int k=minz; k<=maxz; k++)
    {
      Global::progressBar()->setValue((int)(100.0*(float)(k-minz)/(float)(maxz-minz+1)));
      qApp->processEvents();
      
      uchar *mslice = m_maskFileManager.getSlice(k);
      memcpy(m_sliceTemp, mslice, m_width*m_height);
      for(int y=miny; y<=maxy; y++)
	for(int x=minx; x<=maxx; x++)
	  {
	    int idx = (y*m_height + x);
	    if (m_sliceTemp[idx] == tag)
	      m_bitmask.setBit(bidx);

	    bidx++;
	  }
    }

  Global::progressBar()->setValue(100);
  Global::hideProgressBar();
}

void
VolumeMask::createBitmask1(QList<Vec> pbc,
			   QList<Vec> pbs,
			   QBitArray vbitmask,
			   int minx, int miny, int minz,
			   int maxx, int maxy, int maxz)
{
  Global::mainWindowUI()->menubar->parentWidget()->\
    setWindowTitle(QString("Identifying Tagged Region"));
  Global::progressBar()->show();

  int bmx = maxx-minx+1;
  int bmy = maxy-miny+1;
  int bmz = maxz-minz+1;

  int bitSize = bmx*bmy*bmz;
  m_bitmask.resize(bmx*bmy*bmz);

  for(int i=0; i<pbc.size(); i++)
    {      
      Global::progressBar()->setValue((int)(100.0*(float)i/(float)pbc.size()));
      qApp->processEvents();

      float sz = pbs[i].norm();
      int pend = sz+2;	  
      Vec uv = pbs[i].unit();
      Vec pt = pbc[i] - 2*uv;
	  
      for(int pn=0; pn<pend; pn++)
	{
	  int ptx = pt.x;
	  int pty = pt.y;
	  int ptz = pt.z;
	  
	  // proceed only if the voxel is visible
	  int bidx = ((ptz-minz)*bmy*bmx +
		      (pty-miny)*bmx +
		      (ptx-minx));
	  if (vbitmask.testBit(bidx))
	    {
	      for(int px=-1; px<=1; px++)
		for(int py=-1; py<=1; py++)
		  for(int pz=-1; pz<=1; pz++)
		    {
		      // trim the corner voxels
		      // so as to make a more round dilation
		      if (qAbs(px) != qAbs(py) ||
			  qAbs(px) != qAbs(pz))
			{
			  int bidx = ((ptz+pz-minz)*bmy*bmx +
				      (pty+py-miny)*bmx +
				      (ptx+px-minx));
			  if (bidx < bitSize)
			    m_bitmask.setBit(bidx);
			}
		    }
	    }
	  
	  pt += uv;
	}
    }
 
  Global::progressBar()->setValue(100);
  Global::hideProgressBar();
}

void
VolumeMask::createBitmask2(QList<Vec> pbc,
			   int rad,
			   int minx, int miny, int minz,
			   int maxx, int maxy, int maxz)
{
  Global::mainWindowUI()->menubar->parentWidget()->\
    setWindowTitle(QString("Identifying Tagged Region"));
  Global::progressBar()->show();

  int bmx = maxx-minx+1;
  int bmy = maxy-miny+1;
  int bmz = maxz-minz+1;

  int bitSize = bmx*bmy*bmz;
  m_bitmask.resize(bmx*bmy*bmz);

  for(int i=0; i<pbc.size(); i++)
    {      
      Global::progressBar()->setValue((int)(100.0*(float)i/(float)pbc.size()));
      qApp->processEvents();

      int ptx = pbc[i].x;
      int pty = pbc[i].y;
      int ptz = pbc[i].z;

      int pxmin = qMax(minx, ptx-rad);
      int pymin = qMax(miny, pty-rad);
      int pzmin = qMax(minz, ptz-rad);
      int pxmax = qMin(maxx, ptx+rad);
      int pymax = qMin(maxy, pty+rad);
      int pzmax = qMin(maxz, ptz+rad);

      for(int px=pxmin; px<=pxmax; px++)
	for(int py=pymin; py<=pymax; py++)
	  for(int pz=pzmin; pz<=pzmax; pz++)
	    {
	      Vec pt = Vec(px, py, pz);		  
	      float len = (pt-pbc[i]).norm();
	      if (len < rad)
		{
		  // make a spherical dilation
		  int bidx = ((pz-minz)*bmy*bmx +
			      (py-miny)*bmx +
			      (px-minx));
		  if (bidx < bitSize)
		    m_bitmask.setBit(bidx);
		}
	    }
    }
 
  Global::progressBar()->setValue(100);
  Global::hideProgressBar();
}

void
VolumeMask::dilateBitmask(int minx, int miny, int minz,
			  int maxx, int maxy, int maxz)
{
  Global::mainWindowUI()->menubar->parentWidget()->\
    setWindowTitle(QString("Dilating Tag Mask"));
  Global::progressBar()->show();

  int bmx = maxx-minx+1;
  int bmy = maxy-miny+1;

  QBitArray bitcopy = m_bitmask;
  m_bitmask.fill(0);

  int bidx = 0;
  for(int k=minz; k<=maxz; k++)
    {
      Global::progressBar()->setValue((int)(100.0*(float)(k-minz)/(float)(maxz-minz+1)));
      qApp->processEvents();

      for(int y=miny; y<=maxy; y++)
	for(int x=minx; x<=maxx; x++)
	  {
	    if (bitcopy.testBit(bidx))
	      {
		int k0 = qMax(k-1, minz);
		int k1 = qMin(k+1, maxz);
		int y0 = qMax(y-1, miny);
		int y1 = qMin(y+1, maxy);
		int x0 = qMax(x-1, minx);
		int x1 = qMin(x+1, maxx);

		for(int k2=k0; k2<=k1; k2++)
		  for(int y2=y0; y2<=y1; y2++)
		    for(int x2=x0; x2<=x1; x2++)
		      {
			int idx = (k2-minz)*bmy*bmx +
			          (y2-miny)*bmx +
			          (x2-minx);
			m_bitmask.setBit(idx);
		      }
	      }
	    bidx ++;	    
	  }
    }

  Global::progressBar()->setValue(100);
  Global::hideProgressBar();
}

void
VolumeMask::erodeBitmask(int minx, int miny, int minz,
			 int maxx, int maxy, int maxz)
{
  Global::mainWindowUI()->menubar->parentWidget()->\
    setWindowTitle(QString("Eroding Tag Mask"));
  Global::progressBar()->show();

  int bmx = maxx-minx+1;
  int bmy = maxy-miny+1;

  QBitArray bitcopy = m_bitmask;

  int bidx = 0;
  for(int k=minz; k<=maxz; k++)
    {
      Global::progressBar()->setValue((int)(100.0*(float)(k-minz)/(float)(maxz-minz+1)));
      qApp->processEvents();

      for(int y=miny; y<=maxy; y++)
	for(int x=minx; x<=maxx; x++)
	  {
	    if (bitcopy.testBit(bidx))
	      {
		int k0 = qMax(k-1, minz);
		int k1 = qMin(k+1, maxz);
		int y0 = qMax(y-1, miny);
		int y1 = qMin(y+1, maxy);
		int x0 = qMax(x-1, minx);
		int x1 = qMin(x+1, maxx);

		bool ok = true;
		for(int k2=k0; k2<=k1; k2++)
		  for(int y2=y0; y2<=y1; y2++)
		    for(int x2=x0; x2<=x1; x2++)
		      {
			int idx = (k2-minz)*bmy*bmx +
			          (y2-miny)*bmx +
			          (x2-minx);
			ok &= bitcopy.testBit(idx);
		      }
	    

		if (!ok) // surface voxel
		  m_bitmask.setBit(bidx, 0);
	      }

	    bidx ++;	    
	  }
    }

  Global::progressBar()->setValue(100);
  Global::hideProgressBar();
}

bool
VolumeMask::getOpacity(uchar *mask,
		       uchar *tmp,
		       uchar *lut,
		       uchar *tagColors,
		       int x, int y)
{
  unsigned char v,g;
  int idx = (y*m_height + x);
  v = tmp[2*idx];
  g = tmp[2*idx+1];
  
  uchar mval = mask[idx];
  bool op = (lut[4*(256*g + v)+3] > 0);
  
  if (mval == 255) // voxel never visible
    op = false;
  else if (mval > 250)
    // mask[idx] > 250 && mask[idx] < 255
    op = (tagColors[4*mval+3] > 0); // voxel opacity equals tag opacity
  else if (mval > 0)
    {	    
      uchar r = tagColors[4*mval+0];
      uchar g = tagColors[4*mval+1];
      uchar b = tagColors[4*mval+2];
      
      // voxel opacity equals tag opacity for black tag color
      if (r < 2 && g < 2 && b < 2)
	op = (tagColors[4*mval+3] > 0);
      // else only color modulated so take original opacity
    }

  return op;
}

bool
VolumeMask::getOpacity(uchar *mask,
		       uchar *tagColors,
		       int x, int y)
{
  int idx = (y*m_height + x);  
  uchar mval = mask[idx];
  bool op = true;  // assuming voxel has non-zero opacity
  
  if (mval == 255) // voxel never visible
    op = false;
  else if (mval > 250)
    // mask[idx] > 250 && mask[idx] < 255
    op = (tagColors[4*mval+3] > 0); // voxel opacity equals tag opacity
  else if (mval > 0)
    {	    
      uchar r = tagColors[4*mval+0];
      uchar g = tagColors[4*mval+1];
      uchar b = tagColors[4*mval+2];
      
      // voxel opacity equals tag opacity for black tag color
      if (r < 2 && g < 2 && b < 2)
	op = (tagColors[4*mval+3] > 0);
      // else only color modulated so take original opacity
    }

  return op;
}


uchar*
VolumeMask::getDragTexture()
{
  int ncols = m_dragTextureSize.x;
  int nrows = m_dragTextureSize.y;
  int lod = m_dragTextureSize.z;

  int minx = m_dataMin.x;
  int miny = m_dataMin.y;
  int minz = m_dataMin.z;
  int maxz = m_dataMax.z;

  int lenx = m_subvolumeSize.x;
  int leny = m_subvolumeSize.y;
  int lenz = m_subvolumeSize.z;
  int lenx2 = lenx/lod;
  int leny2 = leny/lod;
  int lenz2 = lenz/lod;
  
  if (m_dragTexture) delete [] m_dragTexture;
  m_dragTexture = new unsigned char[m_dragTexWidth*m_dragTexHeight];
  memset(m_dragTexture, 0, m_dragTexWidth*m_dragTexHeight);

  Global::mainWindowUI()->menubar->parentWidget()->\
    setWindowTitle(QString("loading drag volume(%1-%2)").arg(minz).arg(maxz));
  Global::progressBar()->show();

  int maxlenx2 = m_dragTexWidth/ncols;
  int maxleny2 = m_dragTexHeight/nrows;
  int row = 0;
  int col = 0;
  int kslc = 0;

  int nbytes = m_width*m_height;
  uchar *tmp = new uchar[nbytes];

  for(int kslc=0; kslc<lenz2; kslc++)
    {
      int k = minz + kslc*lod;
      Global::progressBar()->setValue((int)(100.0*(float)(k-minz)/(float)(maxz-minz+1)));
      qApp->processEvents();

      //----------------------------------------------
      QFileInfo fi(m_maskFileManager.fileName());
      Global::mainWindowUI()->menubar->parentWidget()->		\
	setWindowTitle(QString("Drishti - mask LOD(%1) %2 from %3").	\
		       arg(lod).arg(k).arg(fi.fileName()));
      
      uchar *mslice = m_maskFileManager.getSlice(k);
      memcpy(m_sliceTemp, mslice, nbytes);
      //----------------------------------------------

      if (lod > 1)
	{
	  int ji=0;
	  for(int j=0; j<leny2; j++)
	    { 
	      int y = miny + j*lod;
	      for(int i=0; i<lenx2; i++) 
		{ 
		  int x = minx + i*lod; 
		  tmp[ji] = m_sliceTemp[y*m_height+x];
		  ji++;
		}
	    }
	  memcpy(m_sliceTemp, tmp, leny2*lenx2);
	}
      else
	{
	  for(int j=0; j<leny2; j++)
	    memcpy(m_sliceTemp + j*lenx2,
		   m_sliceTemp + ((j+miny)*m_height + minx),
		   lenx2);
	}

      int grow = row*maxleny2;
      for(int j=0; j<leny2; j++)
	memcpy(m_dragTexture + (col*maxlenx2 +
				(grow+j)*m_dragTexWidth),
	       m_sliceTemp + (j*lenx2),
	       lenx2);

      if (row == nrows && col > 0)
	QMessageBox::information(0, "ERROR", QString("row, col ?? %1 %2 , %3").	\
				 arg(row).arg(nrows).arg(col));
      col++;
      if (col >= ncols)
	{
	  col = 0;
	  row++;
	}
    }

  //---------------------------------
  // copy last slice
  if (row < nrows && col < ncols)
    {
      int grow = row*maxleny2;
      for(int j=0; j<leny2; j++)
	memcpy(m_dragTexture + (col*maxlenx2 +
				 (grow+j)*m_dragTexWidth),
	       m_sliceTemp + (j*lenx2),
	       lenx2);
    }

  //---------------------------------

  delete [] tmp;

  Global::progressBar()->setValue(100);
  Global::hideProgressBar();


//  QString flnm;
//  flnm = QFileDialog::getSaveFileName(0,
//	   QString("Save raw : %1(%2 %3)").arg(texWidth).arg(ncols).arg(lenx2),
//		   Global::previousDirectory(),
//		   "raw Files (*.raw)");
//
//  if (!flnm.isEmpty())
//    {
//      fstream fout((char *)flnm.toAscii().data(),
//		   ios::out|ios::binary);
//      fout.write((char*)m_dragTexture, ncols*lenx2*nrows*leny2);
//      fout.close();
//    }

  return m_dragTexture;
}

void
VolumeMask::resetSliceTexture(int tw, int th,
			      int tc, int tr,
			      int lenx2, int leny2)
{
  m_texWidth = tw;
  m_texHeight = th;
  m_texColumns = tc;
  m_texRows = tr;

  // create mask file if not present
  if (!m_maskFileManager.exists())
    m_maskFileManager.createFile(true);

  if (m_sliceTexture) delete [] m_sliceTexture;
  m_sliceTexture = new unsigned char[m_texWidth*m_texHeight];
}

uchar*
VolumeMask::getSliceTextureSlab(int minz, int maxz)
{
  int minx = m_dataMin.x;
  int miny = m_dataMin.y;
  int lenx = m_subvolumeSize.x;
  int leny = m_subvolumeSize.y;
  int lenz = m_subvolumeSize.z;
  int lenx2 = lenx/m_svsl;
  int leny2 = leny/m_svsl;
  int lenz2 = lenz/m_svsl;

  int ncols = m_texColumns;
  int nrows = m_texRows;
  
  int maxlenx2 = m_texWidth/m_texColumns;
  int maxleny2 = m_texHeight/m_texRows;

  memset(m_sliceTexture, 0, m_texWidth*m_texHeight);

  //---------------------------------------------------------
  if (m_svsl > 1)
    {
      int kmin = minz/m_svsl;
      int kmax = maxz/m_svsl;
      int kbytes = lenx2*leny2;

      int kslc = 0;
      int row = 0;
      int col = 0;

      // additional slice at the top and bottom
      for(int k=kmin-1; k<=kmax+1; k++)
	{
	  if (k >= 0 && k<lenz2)
	    {
	      uchar *mslice = m_maskFileManager.getSlice(k*m_svsl);
	      int ji=0;
	      for(int j=0; j<leny2; j++)
		{ 
		  int y = miny + j*m_svsl;
		  for(int i=0; i<lenx2; i++) 
		    { 
		      int x = minx + i*m_svsl; 
		      m_sliceTemp[ji] = mslice[y*m_height+x];
		      ji++;
		    }
		}
	    }
	  else
	    memset(m_sliceTemp, 0, kbytes);

	  QFileInfo fi(m_maskFileManager.fileName());
	  Global::mainWindowUI()->menubar->parentWidget()->		\
	    setWindowTitle(QString("Drishti - mask %1 from %2").arg(k*m_svsl).arg(fi.fileName()));


	  int grow = row*maxleny2;
	  for(int j=0; j<leny2; j++)
	    memcpy(m_sliceTexture + (col*maxlenx2 +
				     (grow+j)*m_texWidth),
		   m_sliceTemp + (j*lenx2),
		   lenx2);

	  if (row == nrows && col > 0)
	    QMessageBox::information(0, "ERROR", QString("row, col ?? %1 %2 , %3").\
				     arg(row).arg(nrows).arg(col));


	  col++;
	  if (col >= ncols)
	    {
	      col = 0;
	      row++;
	    }
	  kslc ++;
	}
    
      return m_sliceTexture;
    }
  //---------------------------------------------------------

  //---------------------------------------------------------
  //m_svsl == 1
  //---------------------------------------------------------

  int nbytes = m_width*m_height;
  int kslc = 0;
  int row = 0;
  int col = 0;

  // additional slice at the top and bottom
  //-------------------------------------------------------
  for(int k=minz-1; k<=maxz+1; k++)
    {

      if (k >= 0 && k < m_depth)
	{
	  QFileInfo fi(m_maskFileManager.fileName());
	  Global::mainWindowUI()->menubar->parentWidget()->		\
	    setWindowTitle(QString("Drishti - mask %1 from %2").arg(k).arg(fi.fileName()));

	  uchar *mslice = m_maskFileManager.getSlice(k);
	  memcpy(m_sliceTemp, mslice, nbytes);
	}
      else
	memset(m_sliceTemp, 0, nbytes);

      for(int j=0; j<leny2; j++)
	memcpy(m_sliceTemp + j*lenx2,
	       m_sliceTemp + ((j+miny)*m_height + minx),
	       lenx2);
      
      int grow = row*maxleny2;
      for(int j=0; j<leny2; j++)
	memcpy(m_sliceTexture + (col*maxlenx2 +
				 (grow+j)*m_texWidth),
	       m_sliceTemp + (j*lenx2),
	       lenx2);
      
      if (row == nrows && col > 0)
	QMessageBox::information(0, "ERROR", QString("row, col ?? %1 %2 , %3").	\
				 arg(row).arg(nrows).arg(col));
      
      
      col++;
      if (col >= ncols)
	{
	  col = 0;
	  row++;
	}
	  
      kslc ++;
    }

  return m_sliceTexture;
}

